package security;

import java.io.ByteArrayOutputStream;
import java.io.File;
import java.io.FileInputStream;
import java.io.IOException;
import java.io.ObjectOutput;
import java.io.ObjectOutputStream;
import java.io.RandomAccessFile;
import java.security.GeneralSecurityException;
import java.security.InvalidKeyException;
import javax.crypto.Mac;
import javax.crypto.spec.SecretKeySpec;
import constans.SizeConstans;


/**
 * This class implements the abstract class Signature using the SHA1 algorithm
 */
public class SignatureSHA1 extends Signature{


    /**
     * The size of the key
     */
    public static final int  keySize = 20;

    /**
     * SecretKeySpec object specifies a secret key in a provider-independent fashion
     */
	private SecretKeySpec keySpec;

    /**
     * Mac object implements the functionality of a "Message Authentication Code" (MAC) algorithm.
     */
	private Mac mac;


    /**
     *  Class constructor specifying key
     * 
     * @param keyBytes key
     * @throws java.security.InvalidKeyException If an exception for invalid Keys (invalid encoding, wrong length, uninitialized, etc)  error occurs
     * @throws java.security.GeneralSecurityException If an generic security exception error occurs
     */
    public SignatureSHA1(byte [] keyBytes)
            throws InvalidKeyException, GeneralSecurityException{
		
		if(keyBytes.length!=keySize)
			throw new InvalidKeyException("Invalid key length:"+keyBytes.length+" bytes");
		
		keySpec = new SecretKeySpec(keyBytes,"SHA-1");
		mac = Mac.getInstance("HmacSHA1");
	}		

	protected byte[] signatureFile(File file)
            throws IOException{
		
	
		FileInputStream stream = null;
		try {

            mac.init(keySpec);

			stream = new FileInputStream(file);

			int numRead = 0;
			byte [] buffer = new byte[(int)SizeConstans.KByte *512];
			
			while ((numRead = stream.read(buffer)) >= 0)
				mac.update(buffer, 0, numRead);

			stream.close();			
			
		} catch (IOException e) {
			try{
				if(stream!=null){
					stream.close();
				}
			}catch (IOException e1) {}
			throw new IOException(e);
		} catch (InvalidKeyException e) {}
        
        return mac.doFinal();
	}

	protected byte[] signatureFile(File file, long init, long offset)
            throws IOException{
		
		RandomAccessFile accessFile= null;
		try {

            mac.init(keySpec);
            
			accessFile = new RandomAccessFile(file,"r");
			accessFile.seek(init);
			
			int numRead = 0;
			
			byte [] buffer = new byte[(int)SizeConstans.MByte];
			
			while(init <offset){
				if((offset-init) > (int)SizeConstans.MByte ){
					numRead = (int) accessFile.read(buffer);
					mac.update(buffer, 0, numRead);
				}else{
					numRead = (int) accessFile.read(buffer,0,(int)(offset-init));
					mac.update(buffer, 0, numRead);
				}
				init+=numRead;
			}
			
			accessFile.close();

		} catch (IOException e) {
			try{
				if(accessFile!=null){
					accessFile.close();
				}
			}catch (IOException e1) {}
			throw new IOException(e);
		} catch (InvalidKeyException e) {}

        return mac.doFinal();
	}

	protected byte[] signatureObject(Object obj)
            throws IOException{

		try {
			mac.init(keySpec);
		} catch (InvalidKeyException e) {}
		
		ByteArrayOutputStream bos = null;
		ObjectOutput out = null;

		try {

			bos = new ByteArrayOutputStream();
			out = new ObjectOutputStream(bos);
			
			out.writeObject(obj);
			out.flush();
			
			mac.update(bos.toByteArray());

			out.close();
			out = null;

			bos.close();
			bos = null;		

		} catch (IOException e) {
			try {
				if(out!=null){
					out.close();
					out = null;
				}
			} catch (IOException e1) {}
			try {
				if(bos!=null){
					bos.close();
					bos = null;
				}
			} catch (IOException e1) {}
			throw new IOException(e);
		}

        return mac.doFinal(); 
	}


}
